// Copyright (c) 2005  INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of cgal-python; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; version 2.1 of the License.
// See the file LICENSE.LGPL distributed with cgal-python.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $Id: iterator.h 161 2007-11-02 13:29:41Z spion $
// $URL: svn+ssh://scm.gforge.inria.fr/svn/cgal-python/trunk/cgal-python/include/iterator.h $
//
// Author(s)     : Naceur Meskini
//=========================================================================

#ifndef CGAL_PYTHON_ITERATOR_H
#define CGAL_PYTHON_ITERATOR_H

#include <string>
#include <boost/python.hpp>
#include <iterator>
#include <iostream>

namespace Detail {
  template<typename T> bool type_already_registered()
  {
    using boost::python::objects::registered_class_object;
    using boost::python::type_id;
    
    return registered_class_object(type_id<T>()).get() != 0;
  }
} // end namespace Detail

// to be used only with Edges and points iterators.
template<typename Iterator>
class simple_python_iterator
{
	public:
	typedef typename std::iterator_traits<Iterator>::difference_type
	difference_type;

	simple_python_iterator(std::pair<Iterator, Iterator> p)
		: orig_first(p.first), first(p.first), last(p.second), n(-1) { }
	
	typename std::iterator_traits<Iterator>::value_type next() 
	{ 
		using boost::python::objects::stop_iteration_error;
		if (first == last) stop_iteration_error();
		return *first++;
	}
	difference_type len()
	{ 
		if (n == -1) n = std::distance(first, last);
		return n; 
	}
	
	static void declare(const char* name, const char* docstring = 0)
	{
		using boost::python::class_;
		using boost::python::no_init;
		using boost::python::objects::identity_function;
		if (!Detail::type_already_registered<simple_python_iterator>())
		class_<simple_python_iterator<Iterator> >(name, docstring, no_init)
			.def("__iter__", identity_function())
			.def("__len__", &simple_python_iterator<Iterator>::len)
			.def("next", &simple_python_iterator<Iterator>::next)
		;
	}
	
	private:
	Iterator orig_first;
	Iterator first;
	Iterator last;
	difference_type n;
};
// to be used only with Edges, facets of 3d triangulation circulators
template<typename Circulator>
class simple_python_circulator
{
public:
  simple_python_circulator(std::pair<Circulator, Circulator> p)
    : orig_first(p.first), first(p.first), last(p.second) { }

  typename std::iterator_traits<Circulator>::value_type next() 
  { 
    using boost::python::objects::stop_iteration_error;
    if (first == last) stop_iteration_error();
    return *first++;
  }
//   bool test_is_empty() const
//   { 
// 	return orig_first.is_empty();
//   }

  static void declare(const char* name, const char* docstring = 0)
  {
    using boost::python::class_;
    using boost::python::no_init;
    using boost::python::objects::identity_function;
    if (!Detail::type_already_registered<simple_python_circulator>())
      class_<simple_python_circulator<Circulator> >(name, docstring, no_init)
        .def("__iter__", identity_function())
        .def("next", &simple_python_circulator<Circulator>::next)
// 		.def("is_empty",&simple_python_circulator<Circulator>::test_is_empty,"test if the circulator is empty")
      ;
  }

private:
  Circulator orig_first;
  Circulator first;
  Circulator last;
};
// to be used when exporting all handles 
template<typename Iterator,class Handle>
class Handle_to_py_iterator
{
public:
  typedef typename std::iterator_traits<Iterator>::difference_type
  difference_type;

  Handle_to_py_iterator(std::pair<Iterator, Iterator> p)
    : orig_first(p.first), first(p.first), last(p.second), n(-1) { }

  Handle next() 
  { 
    using boost::python::objects::stop_iteration_error;
    if (first == last) stop_iteration_error();
    return Handle(first++);
  }

  difference_type len()
  { 
	if (n == -1) n = std::distance(first, last);
	return n; 
  }

  static void declare(const char* name, const char* docstring = 0)
  {
    using boost::python::class_;
    using boost::python::no_init;
    using boost::python::objects::identity_function;
    if (!Detail::type_already_registered<Handle_to_py_iterator>())
      class_<Handle_to_py_iterator<Iterator,Handle> >(name, docstring, no_init)
        .def("__iter__", identity_function())
        .def("__len__", &Handle_to_py_iterator<Iterator,Handle>::len)
        .def("next", &Handle_to_py_iterator<Iterator,Handle>::next)
      ;
  }

private:
  Iterator orig_first;
  Iterator first;
  Iterator last;
  difference_type n;
};

// to use when exporting all circulators except Edge_circulator
template<typename Circulator,typename Handle>
class circulator_to_py_iterator
{
public:
  circulator_to_py_iterator(std::pair<Circulator, Circulator> p)
    : orig_first(p.first), first(p.first), last(p.second) { }

  Handle next() 
  { 
    using boost::python::objects::stop_iteration_error;
    if (first == last) stop_iteration_error();
	first++;
    return first.base();
  }
// in 3d case the function is_empty doesn't exist.
//   bool test_is_empty() const
//   { 
// 	return orig_first.is_empty();
//   }

  static void declare(const char* name, const char* docstring = 0)
  {
    using boost::python::class_;
    using boost::python::no_init;
    using boost::python::objects::identity_function;
    if (!Detail::type_already_registered<circulator_to_py_iterator>())
      class_<circulator_to_py_iterator<Circulator,Handle> >(name, docstring, no_init)
        .def("__iter__", identity_function())
        .def("next", &circulator_to_py_iterator<Circulator,Handle>::next)
// 		.def("is_empty",&circulator_to_py_iterator<Circulator>::test_is_empty,"test if the circulator is empty")
      ;
  }

private:
  Circulator orig_first;
  Circulator first;
  Circulator last;
};

#endif // CGAL_PYTHON_ITERATOR_H
